﻿using OF.DistributeService.Core.Common;
using OF.DistributeService.Core.Entity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using OF.DistributeService.Core.Attribute;
using OF.DistributeService.Core;
using System.Collections.ObjectModel;
using System.Web;
using System.Threading;
using ZooKeeperNet;
using System.Web.Http;
using OF.DistributeService.Core.Entity.Service;
using OF.DistributeService.Core.Exception;

namespace OF.DistributeService.Server.Service
{
    public class ZookeeperClusterServer : IDisposable
    {
        private static byte[] EmptyBuffer = new byte[0];
        public static readonly List<string> EmptyList = new List<string>();
        public static readonly ReadOnlyCollection<string> EmptyCollection = new ReadOnlyCollection<string>(EmptyList);
        //private static AutoResetEvent stopEvent = new AutoResetEvent(false);
        private ZooKeeperProxy zk = null;
        private List<ApiServiceAttrMeta> metaList = null;

        public ZookeeperClusterServer()
        {
            ZooKeeperSafeConnectWatcher watcher = new ZooKeeperSafeConnectWatcher();
            var config = AppConfig.Get();
            var timeSpan = new TimeSpan(0, 0, 0, config.ZookeeperSessionTimeSpan);
            zk = new ZooKeeperProxy(config.ZookeeperHostPort, timeSpan, watcher, PublishServiceCore);            
        }

        public void Dispose()
        {
            if (zk != null)
            {
                zk.Dispose();
                zk = null;
            }
        }
            
        public void PublishService(List<ApiServiceAttrMeta> pMetaList)
        {
            metaList = pMetaList;
            PublishServiceCore();
        }

        private void PublishServiceCore()
        {
            if (zk == null || metaList == null || metaList.Count == 0)
            {
                return;
            }
            var config = AppConfig.Get();
            List<string> appGroupPathList = new List<string> { 
                config.OFDistributeServiceZKRootPath,
                config.ApplicationName,
                config.GroupName                
            };
            string appGroupNodePath = string.Join("/", appGroupPathList);
            zk.SafeCreatePath(appGroupNodePath, null);
            string publishPath = appGroupNodePath + "/" + HttpUtility.UrlEncode(config.APIBaseUrl);           
            string[] serviceVersionArray = new string[metaList.Count];
            HashSet<string> serviceHashSet = new HashSet<string>();            
            for (int i1 = 0; i1 < serviceVersionArray.Length; i1++)
            {
                ApiServiceAttrMeta meta = metaList[i1];
                OFDistributeServiceAttribute serviceAttr = meta.ServiceAttribute;
                string serviceName = OFDistributeServiceAttribute.GetServiceName(serviceAttr, meta.InterfaceType);
                serviceVersionArray[i1] = serviceName + ":" + serviceAttr.Version;
                if (!serviceHashSet.Contains(serviceVersionArray[i1]))
                {
                    serviceHashSet.Add(serviceVersionArray[i1]);
                }
                else
                {
                    throw new ServiceNameDuplicateException(serviceVersionArray[i1]);
                }
            }
            if (zk.Exists(publishPath, false) != null)
            {
                SafeDeletePath(zk, publishPath);
            }
            byte[] msgPackBytes = Util.GetMsgPackBytes(serviceVersionArray);
            zk.Create(publishPath, msgPackBytes, Ids.OPEN_ACL_UNSAFE, CreateMode.Ephemeral);
        }



        public static void SafeDeletePath(ZooKeeperProxy zk, string root)
        {
            Stack<string> pathStack = new Stack<string>();
            if (zk.Exists(root, false) != null)
            {
                pathStack.Push(root);
            }
            while (pathStack.Count > 0)
            {
                string path = pathStack.Peek();
                System.Diagnostics.Debug.WriteLine("now visit path:" + path);
                List<string> childList = zk.GetChildren(path, false).ToList();
                if (childList != null && childList.Count > 0)
                {
                    foreach (var child in childList)
                    {
                        pathStack.Push(path + "/" + child);
                    }
                }
                else
                {
                    path = pathStack.Pop();
                    System.Diagnostics.Debug.WriteLine("delete node:" + path);
                    zk.Delete(path, -1);
                }
            }
            
        }
    }
}
